﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace LightControls
{
	public delegate void ListItemClickedHandler( SliderList sender, Control listItem, bool isSelected );

	public partial class SliderList : UserControl
	{

		/// <summary>
		/// Event that clients hooks into to get item clicked events.
		/// </summary>
		public event ListItemClickedHandler ListItemClicked;

		#region Mouse event handlers
		private MouseEventHandler mouseDownEventHandler;
		private MouseEventHandler mouseUpEventHandler;
		private MouseEventHandler mouseMoveEventHandler;
		#endregion
		private Boolean IsHorizontal = false;
		private Boolean IsVertical = false;
		private Int32 OffsetX = 0;
		private Int32 OffsetY = 0;
		private Boolean renderLockFlag = false;
		private Point pLastMousePosition = new Point();
		public List<ISliderItem> Items = new List<ISliderItem>();
		private SliderContainer ItemContainer = new SliderContainer();

		[DefaultValue( false )]
		public Boolean SlideHorizontal { get; set; }

		/// <summary>
		/// c'tor
		/// </summary>
		public SliderList()
		{
			InitializeComponent();

			ItemContainer.Width = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width;
			GetContainerHeight();
			ItemContainer.Location = new Point( 0, 0 );
			this.Controls.Add( ItemContainer );

			//Register Event Handlers
			mouseDownEventHandler = new MouseEventHandler( MouseDownHandler );
			mouseUpEventHandler = new MouseEventHandler( MouseUpHandler );
			mouseMoveEventHandler = new MouseEventHandler( MouseMoveHandler );
		}

		#region Painting Events
		protected override void OnPaintBackground( PaintEventArgs e )
		{
			//Do Nothing
		}

		protected override void OnPaint( PaintEventArgs e )
		{
			using ( var currentRender = new Bitmap( this.Width, this.Height ) )
			using ( var currentRenderGraphics = Graphics.FromImage( currentRender ) )
			{
				currentRenderGraphics.FillRectangle( new SolidBrush( Color.Black ), this.Location.X, this.Location.Y, this.Width, this.Height );
				e.Graphics.DrawImage( currentRender, 0, 0 );
			}
		}
		#endregion

		#region MouseHandling
		private void MouseDownHandler( Object sender, MouseEventArgs e )
		{
            OffsetX = 0;
            OffsetY = 0;
			IsHorizontal = false;
			IsVertical = false;
			pLastMousePosition = new Point( e.X, e.Y );
		}

		private void MouseUpHandler( Object sender, MouseEventArgs e )
		{
            if (!((Math.Abs(OffsetX) > 10) || (Math.Abs(OffsetY) > 10)))
            {
                UserControl temp = (UserControl)GetListItemFromEvent((Control)sender);
                if (temp is ISliderItem)
                {
                    this.ClearSelection();
                    ((ISliderItem)(temp)).isSelected = true;
                    temp.Refresh();
                }
                IsVertical = false;
                IsHorizontal = false;
                this.Refresh();
                this.Invalidate();
            }
		}

		

		private void MouseMoveHandler( Object sender, MouseEventArgs e )
		{
			if ( !renderLockFlag )
			{
				if ( e.Button == MouseButtons.Left)
				{
					renderLockFlag = true;
					OffsetX = ( pLastMousePosition.X - e.X );
					OffsetY = ( ( pLastMousePosition.Y - e.Y ) );
					MoveList();
					this.Refresh();
				}
			}
		}

		/// <summary>
		/// Do the moving of the list during mouseMove
		/// </summary>
		private void MoveList()
		{
			//Check if has defined a direction
			if ( !IsHorizontal && !IsVertical )
			{
				//Condition to Horizontal OffsetX must be greater than OffsetY, also it must support SlideHorizontal
				if ( Math.Abs( OffsetX ) > Math.Abs( OffsetY ) && ( SlideHorizontal ) )
				{
					IsHorizontal = true;
					ItemContainer.Location = new Point( ItemContainer.Location.X - OffsetX, ItemContainer.Location.Y );
				}
				else //it's vertical D'uh!
				{
					IsVertical = true;
					ItemContainer.Location = new Point( ItemContainer.Location.X, ItemContainer.Location.Y - OffsetY );
				}
			}
			else
			{
				//Now that the Direction is verified we don't need to verify again... so here is the move
				if ( IsHorizontal && SlideHorizontal )
					ItemContainer.Location = new Point( ItemContainer.Location.X - OffsetX, ItemContainer.Location.Y );

				if ( IsVertical )
					ItemContainer.Location = new Point( ItemContainer.Location.X, ItemContainer.Location.Y - OffsetY );

				//Finally we must fix the position if it is out of bounds
				if ( ItemContainer.Top > 0 )
				{
					ItemContainer.Location = new Point( ItemContainer.Location.X, 0 );
				}
				else
				{
					if ( ItemContainer.Bottom < this.Height )
					{
						ItemContainer.Location = new Point( ItemContainer.Location.X, this.Height - ItemContainer.Height );
					}
				}
			}
		}
		#endregion

		#region Utils

		/// <summary>
		/// Since the list items can be made up of any number of
		/// <c>Control</c>s this helper method determines the
		/// "root" <c>Control</c> of the list item when a mouse
		/// event has been fired.
		/// </summary>
		private Control GetListItemFromEvent( Control sender )
		{
			if ( sender == this || sender == ItemContainer )
				return null;
			else
			{
				while ( sender.Parent != ItemContainer )
					sender = sender.Parent;

				return sender;
			}
		}

		/// <summary>
		/// Unselect all
		/// </summary>
		private void ClearSelection()
		{
			for ( int i = 0; i< Items.Count; i++ )
			{
				if ( Items[i].isSelected )
				{
					Items[i].isSelected = false;
					((UserControl)(Items[i])).Refresh();
				}
				
			}
		}
		/// <summary>
		/// Determine the Container Height.
		/// </summary>
		private void GetContainerHeight()
		{
			if ( Items.Count > 0 )
			{
				UserControl item = (UserControl)Items[0];
				ItemContainer.Height = Math.Max( this.Height, Items.Count * item.Height );
			}
			else
			{
				ItemContainer.Height = this.Height;
			}
		}

		/// <summary>
		/// Reposition of the Items after some operation
		/// </summary>
		private void RepositionItems()
		{
			if ( Items.Count > 0 )
			{
				UserControl item0 = (UserControl)Items[0];
				Point point0 = new Point( item0.Location.X, item0.Location.Y );
				int heightTotal = 0;
				for ( int i = 1; i < Items.Count; i++ )
				{
					UserControl item = (UserControl)Items[i];
					heightTotal += item.Height;
					item.Location = new Point( point0.X, point0.Y + heightTotal );
				}
			}
			GetContainerHeight();
		}

		/// <summary>
		/// Sum the heights in order to get a Y coordinate
		/// </summary>
		/// <returns></returns>
		private Int32 SumHeight()
		{
			UserControl item0 = (UserControl)Items[0];
			Int32 value = 0;
			value = item0.Location.Y;
			for ( int i = 1; i < Items.Count; i++ )
			{
				UserControl item = (UserControl)Items[i];
				value += item.Height;
			}
			if ( value > 0 )
			{
				ItemContainer.Height = Math.Max( this.Height, value );
			}

			return value;
		}
		#endregion

		#region ItemManipulation
		public void AddItem( ISliderItem item )
		{
			Items.Add( item );
			UserControl ic = (UserControl)item;
			ic.Width = this.Width;
			ic.Location = new Point( 0, SumHeight() );
			ItemContainer.Controls.Add( ic );
			GetContainerHeight();
			SetHandlers( ItemContainer, mouseDownEventHandler, mouseUpEventHandler, mouseMoveEventHandler );
			this.Invalidate();
		}

		public void RemoveItem( int index )
		{
			//Check if there are any Items left
			if ( Items.Count - 1 > index )
			{
				( (UserControl)( Items[index + 1] ) ).Location = new Point( ( (UserControl)( Items[index + 1] ) ).Location.X, ( (UserControl)( Items[index + 1] ) ).Location.Y - ( (UserControl)( Items[index] ) ).Height );
			}
			ItemContainer.Controls.Remove( ( (UserControl)( Items[index] ) ) );
			Items.Remove( Items[index] );
			this.Invalidate();
			GetContainerHeight();
			RepositionItems();

		}

        public void RemoveAll()
        {
            Items.Clear();
            ItemContainer.Controls.Clear();
            this.Invalidate();
			GetContainerHeight();
			RepositionItems();
        }

        /// <summary>
        /// Gets the currently selected item or null if none
        /// </summary>
        public ISliderItem SelectedItem
        {
            get
            {
                foreach (ISliderItem item in Items)
                {
                    if (item.isSelected)
                    {
                        return item;
                    }
                }
                return null;
            }
        }
		#endregion

		public void SetHandlers( Control control, MouseEventHandler mouseDownEventHandler, MouseEventHandler mouseUpEventHandler, MouseEventHandler mouseMoveEventHandler )
		{
			control.MouseDown -= mouseDownEventHandler;
			control.MouseUp -= mouseUpEventHandler;
			control.MouseMove -= mouseMoveEventHandler;

			control.MouseDown += mouseDownEventHandler;
			control.MouseUp += mouseUpEventHandler;
			control.MouseMove += mouseMoveEventHandler;

			foreach ( Control childControl in control.Controls )
			{
				SetHandlers( childControl, mouseDownEventHandler, mouseUpEventHandler, mouseMoveEventHandler );
			}
		}

		private void timer1_Tick( object sender, EventArgs e )
		{
			renderLockFlag = false;
			this.Refresh();
		}
	}
}